# To change this template, choose Tools | Templates
# and open the template in the editor.

__author__ = "furoscame"
__date__ = "29.12.11 23:23"

from mosal_message import MosalMessage

debug = False

class MosalA:
    """
    Produces correct MosalA Messages from Bosal strings
    """
    def __init__(self):
        self.id = "07"
        self.PAK = "\xAA"
        self.RMA = "}"
        self.BEL = "\x87"   # ^BEL with bit 7 set see MOSAL spec.
        self.time_out = 0.4
        self.piece_table = {'0':'e', '1':'P', '2':'N', '3':'B', '4':'R', '5':'Q', '6':'K', '9':'p', 'A':'n', 'B':'b', 'C':'r', 'D':'q', 'E':'k'}
        pass
    pass

    def get_level_cmd(self):
        return '05' + self.id + '100008'
    pass

    def get_position_cmd(self):
        return '05' + self.id + '10000E'
    pass

    def get_beep_cmd(self):
        return "06" + self.id + "1D001648"
    pass

    def get_key_cmd(self, key):
        key = 128 + key
        keystr = '{:02X}'.format(key)
        #                                                 probably something to set
        message = "06" + self.id + "04" + "00" + keystr + "00"
        return message
    pass

    def get_open_cmd(self):
        return '06' + self.id + '01' + '00' + '55AA'
    pass

    def get_disconnect_cmd(self):
        return '04' + self.id + '02' + '00'
    pass

    def get_csa_cmd(self):
        return '05' + self.id + '100011'
    pass

    def get_data_area_cmd(self):
        return '07' + self.id + '10001C' + '2E' + '0A'
    pass

    def get_error_cmd(self):
        return '04' + self.id + '0300'
    pass

    def get_message_from_mosal_string(self, message):
        self.mosal_message = MosalMessage("Unkown")
        message = message.rstrip('\r\n')
        # count length and check if wrong remove first byte
        if self._check_length(message) == False:
            l = len(message) - 2
            message = message[2:2 + l]
            #length = message[0:0 + 2]
            if self._check_length(message) == False:
                return self.mosal_message


        # standard id op ww
        #id = message[2:2+2]
        op = message[4:4 + 2]
        if debug == True:
            print ("op", int(op,16))
        
        
        # parse op
        if op == "00":
            self.mosal_message = MosalMessage("Protocol")
        elif op == "01":
            self.mosal_message = MosalMessage("Open")
            pass
        elif op == "02":
            # disconnect not possible because it is only input
            pass
        elif op == "03":
            self.mosal_message = MosalMessage("Error")
            pass
        elif op == "04":
            self.mosal_message = MosalMessage("Keypress")
            pass
        elif op == "05":
            self.mosal_message = MosalMessage("Setup")
            self.mosal_message.set('fen_str',self.parse_position(message+ "0000")) # you have to add the the move count
            pass
        elif op == "06":
            self.mosal_message = MosalMessage("MoveMade")
            self.mosal_message.set('movecount', int(message[8:8 + 4],16))
            self.mosal_message.set('move', self._to_algebraic_move(message[12:12 + 6]))
            pass
        elif op == "07":
            self.mosal_message = MosalMessage("Takeback")
            pass
        elif op == "08":
            self.mosal_message = MosalMessage("Setlevel") #0E010800 01010201020102D16701
            level = message[10:10 + 2]
            lev = int(level, 16)
            if debug == True:
                print ("level ", lev)
            self.mosal_message.set('level',lev)
            pass
        elif op == "09":
            #reserved
            pass
        elif op == "0A":
            #rserved
            pass
        elif op == "0B":
            self.mosal_message = MosalMessage("SearchFinished")
            pass
        elif op == "0C":
            self.mosal_message = MosalMessage("InfoArea")
            # d1 is not usable for me
            self.mosal_message.set('val', int(message[12:12 + 4], 16))
            self.mosal_message.set('depth', int(message[16:16 + 2], 16))
            self.mosal_message.set('val_led', int(message[18:18 + 2], 16))
            pass
        elif op == "0D":
            self.mosal_message = MosalMessage("Library")
            pass
        elif op == "0E":
            self.mosal_message = MosalMessage("Position")
            self.mosal_message.set('fen_str',self.parse_position(message))
            pass
        elif op == "0F":
            self.mosal_message = MosalMessage("Replay")
            pass
        elif op == "10":
            self.mosal_message = MosalMessage("Request")
            pass
        elif op == "11":
            self.mosal_message = MosalMessage("CSA")
            pass
        elif op == "12":
            self.mosal_message = MosalMessage("SecondCount")
            pass
        elif op == "13":
            #reserved
            pass
        elif op == "14":
            #reserved
            pass
        elif op == "15":
            self.mosal_message = MosalMessage("BeginSearch")
            pass
        elif op == "16":
            self.mosal_message = MosalMessage("InterruptSearch")
            pass
        elif op == "17":
            self.mosal_message = MosalMessage("AbortSearch")
            pass
        elif op == "18":
            self.mosal_message = MosalMessage("MoveRecognized")
            self.mosal_message.set('movecount', int(message[8:8 + 4],16))
            self.mosal_message.set('move', self._to_algebraic_move(message[12:12 + 6]))
            pass
        elif op == "19":
            #reserved
            pass
        elif op == "1A":
            self.mosal_message = MosalMessage("UserBook")
            pass
        elif op == "1B":
            self.mosal_message = MosalMessage("AddIO")
            pass
        elif op == "1C":
            self.mosal_message = MosalMessage("DataArea")
            #0F011C006400000000000000000000
            self.mosal_message.set('address', message[8:8 + 2])
            l = len(message) - 10
            self.mosal_message.set('data', message[10:10 + l])
            pass
        elif op == "1D":
            self.mosal_message = MosalMessage("Beep")
            pass
        elif op == "1E":
            self.mosal_message = MosalMessage("Stopping")
            pass
        elif op == "1F":
            #reserved
            pass
        elif op == "20":
            #send across
            pass
        else:
            self.mosal_message = MosalMessage("Unkown")
            if debug == True:
                print ("Unknown")
        pass

        return self.mosal_message
    pass

    def parse_position(self, message):
        """
            Construct a FEN String
        """

        pos_str = ""
        #length =  message[0:0+2]#27
        l = len(message) - 8
        message = message[8:8 + l]
        #print ("nach lenght" ,message)
        status_f = message[0:0 + 2]# 0F

        l = len(message) - 2
        message = message[2:2 + l]
        if debug == True:
            print (message)
        field = [[0 for row in range(8)] for col in range(8)]
        for row in range(0, 8):
            for col in range(0, 8):
                piece_code = message[0:0 + 1]
                #print ("piece_code", piece_code)
                # cut head
                l = len(message) - 1
                message = message[1:1 + l]

                piece = self.piece_table[piece_code]
                field[row][col]  = piece
                #print (field)
            pass
        pass

        i = 0
        for col in range(7, -1, -1):
            for row in range(0, 8):
                #print (row, col)
                if field[row][col] == 'e':
                    i += 1
                else:
                    if i > 0:
                        pos_str += str(i)
                        i = 0
                        pos_str += field[row][col]
                    else:
                        pos_str += field[row][col]
                pass
            pass
            if i > 0:
                pos_str += str(i)
                i = 0
            if col > 0:
                pos_str += '/'
        pass
        status_str = self.get_status(status_f)
        pos_str += status_str
        ep_status = ' - '  # there is no ep status in OSA :-(
        pos_str += ep_status

        move_cnt = int(message, 16) + 1 # as well no 50 moves rule counter
        move_cnt = move_cnt / 2 + move_cnt % 2

        pos_str += '0 ' + str(move_cnt) + ' '
        if debug == True:
            print ("The Fen:", pos_str)
        return pos_str
        pass
    pass

    def get_status(self, status):

        s = int(status, 16)
        if debug == True:
            print ("status", s)
        ret = " "
        if s & 128: # bit 3
            ret = ret + 'b '
        else:
            ret = ret + 'w '
        if s & 64: # bit
            pass # tags position from setup, not play
        if s & 8: # bit 3
            ret = ret + 'K'
        if s & 4: # bit 2
            ret = ret + 'Q'
        if s & 2: # bit 1
            ret = ret + 'k'
        if s & 1:  # bit 0
            ret = ret + 'q'
        return ret
        pass
    pass

    def get_mosal_string(self, mosal_message):
        message = ""
        if debug == True:
            print ("get_mosal_string", mosal_message.message)
        if mosal_message.message == "Open":
            message = self.id + "01" + "00" + "55AA"

        elif mosal_message.message == "Keypress":
            message = self.id + "04" + "00" + hex( mosal_message.get('key')[2:].upper()) + "00"

        elif mosal_message.message == "MoveMade":
            mosal_move = self.to_mosal_move(mosal_message.get('move'))
            #                           ww    move count 3B         endofgame time
            message = self.id + "06" + "00" + "0000" + mosal_move + "00" + "0000"
            #   0B         07    06     00     0000    3133          00     0000
        elif mosal_message.message == "Takeback":
            pass
        elif mosal_message.message == "Setlevel":
            #             E07    08     8001                        00                                     0000                              0000
            #                          ww off
            message = self.id + '08' + '8001' + '{:02X}'.format(mosal_message.get('level')) + '{:04X}'.format(mosal_message.get('wtime')) \
                      + '{:04X}'.format(mosal_message.get('btime')) + '{:04X}'.format(mosal_message.get('movetime')) \
                      + '{:02X}'.format(mosal_message.get('max_level'))# + '{:02X}'.format(mosal_message.int_level)
            #0E070880010000000000              0000                                   77                                       70
        elif mosal_message.message == "SearchFinished":
            mosal_move = self._to_mosal_move_2byte(mosal_message.get('move'))
            #            0601    0B     00    0605                              2B
            message = self.id + "0B" + "00" + mosal_move
            if debug == True:
                print ("message move", message)

        elif mosal_message.message == "InfoArea":
            main_var = ""
            i=0

            for move in mosal_message.get('main_var'):
                i +=1
                m = self._to_mosal_move_2byte(move)
                main_var += m
                #print (main_var, m)
                if i == 19:
                    break # not more than 20 moves


            main_var += "FF"
            l = len(main_var)
            while l < 40:
                main_var += "F"
                l += 1
            #          3401      0C     00     01           80                                                       0F0000000605 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00000000000000000000000000000000000000000000000000
            #           1B070    C0     0      01           80                                                       F000                               00 06 444FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
            #            1E07    0C     00    00             80                                             0F00
            #                           ww    offset?!?   solid flash                                                pos value                          curr search depth
            message = self.id + "0C" + "00" + "01"      + '{:02X}'.format(mosal_message.get('DMFLGS'))        + '{:04X}'.format(mosal_message.get('score'))   \
                      + '{:02X}'.format(mosal_message.get('depth')) + '{:02X}'.format(mosal_message.get('val_led'))   + main_var
                      #                       00                                    00                        4644FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
                #                  which val led to show   variation to show

        elif mosal_message.message == "Request":
            message = self.id + "10" + "00" + mosal_message.get('opcode')

        elif mosal_message.message == "CSA":
            if debug == True:
                print ("get_mosal_string in CSA", mosal_message.message)
            # dont forget to freeze the display / info changes / moves
            #search_master = self.get_search_master(mosal_message.search_master)
            #                    op     wwof    all you want so see                  serial dev    60 sec timeout internal status
            message = self.id + "11" + "8001" + mosal_message.get('serial_search_master')   + self.id      + "3C"       #  + "00"
            #          parallel SM ID TO  nothing to set there because we a serial
            #print ("CSA:", message)

        elif mosal_message.message == "BeginSearch":
            message = self.id + '15' + '00'

        elif mosal_message.message == "InterruptSearch":
            message = self.id + '16' + '00'

        elif mosal_message.message == "AbortSearch":
            message = self.id + '17' + '00'

        elif mosal_message.message == "MoveRecognized":
            message = self.id + '18' + '00'

        elif mosal_message.message == "DataArea":
            #                           ww     my fix adress
            message = self.id + '1C' + '80' + '2E'            + mosal_message.get('data')
            pass
        elif mosal_message.message == "Beep":
            message = self.id + "1D" + "00" + '{:02X}'.format(mosal_message.get('duration')) + '{:02X}'.format(mosal_message.get('frequency'))

        else:
            print ("Nothing defined")

        message = self.prepend_length_byte(message)
        return message
    pass

    def _check_length(self, message):
        """
        this method checks if the length of message is correct
        """
        length = message[0:0 + 2]
        
        if int(length, 16) == (len(message) / 2):
            return True
        else:
            print ("Error at message length", length[0])
            return False
        pass

    pass

    def _to_algebraic_move(self, mosal_move):
        """
        this method converts a MOSAL move into an algebraic move
        """

        ret = ""
        for i in range(2):
            ret += chr(int(mosal_move[2 * i: 2 * i + 1], 16) + ord('a'))
            ret += chr(int(mosal_move[2 * i + 1: 2 * i + 1 + 1], 16) + ord('1'))

        p = int(mosal_move[4: 4 + 2], 16)
        if p == 0x98:
            ret += 'q'
        elif  p == 0x90:
            ret += 'r'
        elif  p == 0x88:
            ret += 'b'
        elif p == 0x80:
            ret += 'n'
        else:
            ret += ' '

        if debug == True:
            print ("san move: ", ret)

        return ret
    pass

    def _to_mosal_move_3byte(self, move):
        """
        this method converts an algebraic move into a MOSAL 3 byte move
        """
        ret = ""
        move = move.strip('\r\n')
        for i in range(2):
            ret += '{:01X}'.format(ord(move[2 * i]) - ord('a'))
            ret += '{:01X}'.format(ord(move[2 * i + 1]) - ord('1'))
            
        if len(move) > 4:
            # promotion
            if move[4] == 'q':
                ret += '{:02X}'.format(0x98)
                pass
            elif  move[4] == 'r':
                ret += '{:02X}'.format(0x90)
                pass
            elif  move[4] == 'b':
                ret += '{:02X}'.format(0x81)
                pass
            elif move[4] == 'n':
                ret += '{:02X}'.format(0x80)
        else:
            ret += '{:02X}'.format(0x00)
            
        return ret
    pass

    def _to_mosal_move_2byte(self, move):
        """
        this method convert an algebraic move into a 2 byte MOSAL move
        """
        ret = ""
        move = move.strip('\r\n')
        for i in range(2):
            ret += '{:01X}'.format(ord(move[2 * i]) - ord('a'))
            ret += '{:01X}'.format(ord(move[2 * i + 1]) - ord('1'))

        if len(move) > 4:
            # promotion
            if move[4] == 'q':
                promo_bits = 0x88
                pass
            elif  move[4] == 'r':
                promo_bits = 0x80
                pass
            elif  move[4] == 'b':
                promo_bits = 0x08
                pass
            elif move[4] == 'n':
                promo_bits = 0x00

            i = int(ret[2:2+2],16)
            i = i | (promo_bits & 0xFF)

            ret = ret[:2] + '{:02X}'.format(i)
            if debug == True:
                print ("mosal move", ret)

        return ret
    pass

    def prepend_length_byte(self, message):
        l = len(message) / 2 + 1
        message = '{:02X}'.format(l) + message
        return message
        pass

    pass

pass

